#include <stdarg.h>
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdint.h>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include "MyException.h"
#include "mylog.h"

const char* const mylog::strLogName = "default";
const char* mylog::LevelTag[] = { "DEBUG", "ROUTN", "ALARM", "FATAL", };

mylog* mylog::m_mylog = new mylog();

mylog :: mylog()
{
    pthread_mutex_init(&m_lock, NULL);
    m_level = ROUTN;
    m_file_size = 1073741824;
    snprintf(m_path, sizeof(m_path), "./log/%s.log", strLogName);
    m_log_fd = -1;
    m_LastDayTime = DayTimeNow();
    LogFileCheckOpen();
}

mylog* mylog::getInstance()
{
    return m_mylog;
}

void mylog :: setlog(const uint32_t level, const uint32_t size, const char* logname)
{
    m_level = (level > (uint32_t)FATAL) ? (uint32_t)ROUTN : level;
    m_file_size = size > 1073741824 ? 1073741824 : size;
    const char* mylogname = (logname[0] != 0 ) ? logname : strLogName;
    MyThrowAssert(NULL == strchr(mylogname, '/'));
    if (0 != strcmp(mylogname, strLogName))
    {
        close(m_log_fd);
        m_log_fd = -1;
        char  logpath[LogNameMaxLen];
        snprintf(logpath, sizeof(logpath), "%s.%d", m_path, m_LastDayTime);
        remove(logpath);
    }
    snprintf(m_path, sizeof(m_path), "./log/%s.log", mylogname);
    LogFileCheckOpen();
    fprintf(stderr, "mylog Level[%u] SetLevel[%u] Path[%s] Size[%u]\n",
            m_level, level, m_path, m_file_size);
    return;
}

void mylog :: WriteNByte(const int fd, const char* buff, const uint32_t size)
{
    int32_t  left   = size;
    uint32_t offset = size;
    while (left > 0)
    {
        int len = write(fd, buff+offset-left, left);
        if (len == -1 || len == 0)
        {
            return;
        }
        left -= len;
    }
}
void mylog :: WriteLog(const uint32_t mylevel, const char* file,
        const uint32_t line, const char* func, const char *format, ...)
{
    pthread_mutex_lock(&m_lock);
    //    LogFileCheckOpen();
    LogFileSwitchCheck();
    va_list args;
    va_start(args, format);
    char buff[LogContentMaxLen];
    uint32_t pos = 0;
    if (mylevel < m_level) {
        pthread_mutex_unlock(&m_lock);
        return;
    }
    uint32_t  level = (mylevel > (uint32_t)FATAL) ? (uint32_t)FATAL : mylevel;
    pos =  snprintf(&buff[pos], LogContentMaxLen-pos, "%s ", LevelTag[level]);
    pos += snprintf(&buff[pos], LogContentMaxLen-pos, "%s ", GetTimeNow());
    pos += snprintf(&buff[pos], LogContentMaxLen-pos, "%lu ", pthread_self());
    pos += snprintf(&buff[pos], LogContentMaxLen-pos, "file[%s:%u] ", file, line);
    pos += snprintf(&buff[pos], LogContentMaxLen-pos, "func[%s] ", func);
    vsnprintf(&buff[pos], LogContentMaxLen-pos-2, format, args);
    va_end(args);
    pos = strlen(buff);
    buff[pos] = '\n';
    buff[pos+1] = '\0';
    WriteNByte(m_log_fd, buff, pos+1);
    pthread_mutex_unlock(&m_lock);
}

void mylog :: LogFileCheckOpen()
{
    char  tmppath[LogNameMaxLen];
    char  curpath[LogNameMaxLen];
    if (NULL == getcwd(curpath, sizeof(curpath))) {
        fprintf(stderr, "Get Current Dir Fail.\n");
        return;
    }
    char  logpath[LogNameMaxLen];
    snprintf(logpath, sizeof(logpath), "%s.%d", m_path, DayTimeNow());
    char* p = NULL;
    if (0 != access(logpath,F_OK)) {
        snprintf(tmppath, sizeof(tmppath), "%s", logpath);
        p = strrchr(tmppath, '/');
        if (p) {
            *p = '\0';
            Mkdirs(tmppath);
        }
        if (m_log_fd >= 0){
            close(m_log_fd);
            m_log_fd = -1;
        }
    }
    chdir(curpath);
    if (m_log_fd < 0) {
        m_log_fd = open(logpath, O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
        if (m_log_fd < 0) {
            fprintf(stderr, "FILE[%s:%u] create logfile[%s] fail. msg[%m]\n",
                    __FILE__, __LINE__, logpath);
            while(0 != raise(SIGKILL)){}
        }
    }
}

void mylog :: LogFileSwitchCheck()
{
    char  curpath[LogNameMaxLen];
    uint32_t timenow = DayTimeNow();
    if (timenow != m_LastDayTime)
    {
        close(m_log_fd);
        snprintf(curpath, sizeof(curpath), "%s.%d", m_path, timenow);
        m_log_fd = open(curpath, O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
        if (m_log_fd < 0)
        {
            fprintf(stderr, "FILE[%s:%u] create logfile[%s] fail. msg[%m]\n", __FILE__, __LINE__, curpath);
            while(0 != raise(SIGKILL)){}
        }
        m_LastDayTime = timenow;
    }
}

const char* mylog :: GetTimeNow()
{
    time_t now;
    struct tm mytm;

    m_timebuff[0] = 0;
    time(&now);
    localtime_r(&now, &mytm);
    mytm.tm_year += 1900;
    sprintf(m_timebuff, "%02d-%02d %02d:%02d:%02d",
            mytm.tm_mon+1, mytm.tm_mday, mytm.tm_hour, mytm.tm_min, mytm.tm_sec);

    return  m_timebuff;
}

void mylog :: Mkdirs(const char* dir)
{
    char tmp[1024];
    char *p = NULL;
    if (strlen(dir) == 0 || dir == NULL) {
        return;
    }
    memset(tmp, '\0', sizeof(tmp));
    strncpy(tmp, dir, strlen(dir));
    if (tmp[0] == '/') {
        p = strchr(tmp + 1, '/');
    } else {
        p = strchr(tmp, '/');
    }

    if (p) {
        *p = '\0';
        mkdir(tmp, 0777);
        chdir(tmp);
    } else {
        mkdir(tmp, 0777);
        chdir(tmp);
        return;
    }
    Mkdirs(p + 1);
}

uint32_t mylog :: DayTimeNow()
{
    time_t now;
    struct tm mytm;
    uint32_t timenow = 0;

    time(&now);
    localtime_r(&now, &mytm);
    mytm.tm_year += 1900;
    // 2011122915
    timenow = mytm.tm_year*1000000 + (mytm.tm_mon+1)*10000 + mytm.tm_mday*100 + mytm.tm_hour;
    return  timenow;
}
